//
// Created by iris on 24-11-3.
//

#ifndef ASTBASE_H
#define ASTBASE_H

#include <map>
#include <string>
#include <utility>
#include <memory>
#include <variant>
#include <vector>


namespace ast {
    using std::string;
    using std::pair;

    enum class ASTOperator {
        // unary
        AddressOf, Deref, Positive, Negative, BitNot, Not,
        // prefix
        PlusPlus, MinusMinus, Sizeof,
        //postfix
        Subscript, FunctionCall, PostPlusPlus, PostMinusMinus,
        // multiplicative -- left
        Times, Div, Mod,
        // additive
        Add, Sub,
        // shift
        LeftShift, RightShift,
        // relational
        Less, LessEqual, Greater, GreaterEqual,
        // equality
        Equal, NotEqual,
        // bit
        And, Xor, Or,
        // logical
        LogicalAnd, LogicalOr,
        // Conditional
        Question, Colon,
        // Assignment
        Assign, MulAssign, DivAssign, ModAssign, AddAssign, SubAssign, LeftShiftAssign, RightShiftAssign, BitAndAssign,
        BitOrAssign, BitXorAssign,

        // default
        None,
    };

    struct ASTBase {
        enum ASTKind {
            UnreachableBase = 0,

            Declaration,
            Typedef,
            Function,
            // Value,
            BuiltinType,
            UserDefinedType,
            UserDefinedTypeStruct,
            UserDefinedTypeUnion,
            UserDefinedTypeEnum,
            UserDefinedTypeEnd,
            DeclarationEnd,

            Expression,
            Primary,
            Unary,
            Cast,
            Multiplicative,
            Additive,
            Shift,
            Relational,
            Equality,
            And, ExclusiveOr, InclusiveOr,
            LogicalAnd, LogicalOr,
            Conditional,
            Assignment,
            InitializerList,
            Postfix,
            ExpressionEnd,

            Statement,
            Compound,
            ExpressionStatement,
            Return,
            IfElse,
            While,
            Break,
            Continue,
            StatementEnd
        };

        const ASTKind Kind;

        explicit ASTBase(const ASTKind kd) : Kind(kd) {
        }

        virtual ~ASTBase() = default;

        [[nodiscard]] ASTKind getKind() const { return Kind; }
    };

    struct Declaration : ASTBase {
        std::string name;
        bool isConst;

        Declaration(const ASTKind kd, string name, bool isConst) : ASTBase(kd), name(std::move(name)),
                                                                   isConst(isConst) {
        }

        [[nodiscard]] bool isConstDecl() const { return isConst; }

        static bool classof(const ASTBase *base) {
            return base->getKind() < DeclarationEnd && base->getKind() >= ASTKind::Declaration;
        }
    };

    struct Expression : ASTBase {
        explicit Expression(const ASTKind kd) : ASTBase(kd) {
        }

        static bool classof(const ASTBase *cu) {
            return cu->getKind() < ExpressionEnd && cu->getKind() >= ASTKind::Expression;
        }
    };

    struct Statement : ASTBase {
        explicit Statement(const ASTKind kd) : ASTBase(kd) {
        }

        static bool classof(const ASTBase *cu) {
            return cu->getKind() < StatementEnd && cu->getKind() >= ASTKind::Statement;
        }
    };

    // struct ASTValue final : Declaration {
    //     // symbol or literal
    //     using value_t = std::variant<std::string, int, float>;
    //
    //     value_t value;
    //
    //     ASTValue(string name, value_t value) : Declaration(Value, std::move(name)), value(std::move(value)) {
    //     }
    //
    //     static bool classof(const ASTBase *base) {
    //         return base->getKind() == Value;
    //     }
    // };


    namespace type {
        inline struct Int {
        } AST_Int;

        inline struct Float {
        } AST_Float;

        inline struct Void {
        } AST_Void;

        struct Pointer {
            using BaseAndUserDefinedType = std::variant<Int *, Float *, Void *, Declaration *>;

            int depth;
            BaseAndUserDefinedType base;
        };

        using ASTType = std::variant<Int *, Float *, Void *, Pointer *, Declaration *>;

        constexpr ASTType getInt() { return &AST_Int; }
        constexpr ASTType getFloat() { return &AST_Float; }
        constexpr ASTType getVoid() { return &AST_Void; }

        inline std::map<std::string, Declaration *> UserDefinedTypes;
    }

    // inline ASTBase *root;
    inline std::vector<ASTBase *> *root;
}
#endif //ASTBASE_H
